package com.tool.app.util.json.databind;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;

import java.io.IOException;
import java.time.DateTimeException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;

/**
 * 自定义 LocalDateTime 序列化器。
 * 支持字段上的 @JsonFormat 注解来指定日期格式和时区；
 * 如果没有注解，则默认输出时间戳（毫秒）。
 */
public class CustomLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> implements ContextualSerializer {

    private static final ZoneId DEFAULT_ZONE_ID = ZoneId.systemDefault();

    /**
     * 用于格式化的 DateTimeFormatter。
     */
    private final DateTimeFormatter formatter;

    /**
     * 用于转换时间的时区。
     */
    private final ZoneId zoneId;

    /**
     * 默认构造方法，使用系统默认时区，且不进行格式化（输出时间戳）。
     */
    public CustomLocalDateTimeSerializer() {
        this(null, DEFAULT_ZONE_ID);
    }

    /**
     * 带参数构造方法。
     *
     * @param formatter 时间格式化器，null 表示输出时间戳
     * @param zoneId    指定时区，null 表示使用系统默认时区
     */
    public CustomLocalDateTimeSerializer(DateTimeFormatter formatter, ZoneId zoneId) {
        this.formatter = formatter;
        this.zoneId = zoneId == null ? DEFAULT_ZONE_ID : zoneId;
    }

    @Override
    public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (value == null) {
            gen.writeNull();
            return;
        }

        ZonedDateTime zonedDateTime = value.atZone(zoneId);

        if (formatter != null) {
            gen.writeString(formatter.format(zonedDateTime));
        } else {
            gen.writeNumber(zonedDateTime.toInstant().toEpochMilli());
        }
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
        if (property == null) {
            return this;
        }

        JsonFormat annotation = property.getMember().getAnnotation(JsonFormat.class);

        if (annotation == null) {
            return this;
        }

        // 解析 pattern
        String pattern = annotation.pattern();
        if (pattern == null || pattern.isEmpty()) {
            throw new IllegalArgumentException(
                    "@JsonFormat pattern cannot be empty on field: " + property.getName());
        }

        // 解析时区
        ZoneId zoneIdToUse;
        String timezone = annotation.timezone();
        if (JsonFormat.DEFAULT_TIMEZONE.equals(timezone)) {
            zoneIdToUse = ZoneId.systemDefault();
        } else {
            try {
                zoneIdToUse = ZoneId.of(timezone);
            } catch (DateTimeException e) {
                throw new IllegalArgumentException(
                        "Invalid timezone '" + timezone + "' on field: " + property.getName(), e);
            }
        }

        // 解析格式化器
        DateTimeFormatter formatter;
        try {
            formatter = DateTimeFormatter.ofPattern(pattern);
        } catch (IllegalArgumentException | DateTimeParseException e) {
            throw new IllegalArgumentException(
                    "Invalid date format pattern '" + pattern + "' on field: " + property.getName(), e);
        }

        return new CustomLocalDateTimeSerializer(formatter, zoneIdToUse);
    }
}